package ws

import (
	"encoding/json"
	"fmt"
	"net/http"
	"sync"
	"sync/atomic"

	"github.com/gorilla/websocket"
	"starsine.cn/appTpl/cmn"
	. "starsine.cn/appTpl/cmn/defs"
	"starsine.cn/appTpl/cmn/logs"
	"starsine.cn/appTpl/cmn/timermgr"
	"starsine.cn/appTpl/svccenter/registry/restful/chan2svc"
	. "starsine.cn/appTpl/svccenter/registry/restful/openapi"
	. "starsine.cn/appTpl/svccenter/registry/restful/ws-server/wsserver"
)

type WsMng struct {
	sn        atomic.Int32
	timerID   int64
	wsConnMap map[*websocket.Conn]*WsServer

	sync.RWMutex
	appCtxt *AppContext
	tmgr    *timermgr.TimerMgr
}

// var wsMng *WsMng = nil

func CreateWsMng(appCtxt *AppContext) *WsMng {
	p := new(WsMng)

	p.timerID = -1
	p.sn.Store(0)

	p.wsConnMap = make(map[*websocket.Conn]*WsServer)

	p.appCtxt = appCtxt
	p.tmgr = cmn.GetTm()

	return p
}

func (p *WsMng) Start() {
	p.wsHeartbeat()

	logs.Debugf("WsMng start.")
}

func (p *WsMng) wsHeartbeat() {
	if p.timerID != -1 {
		return
	}

	p.timerID = p.tmgr.AddPeriodTimer(5, timermgr.NewOnTimeOut(p.heartbeatCB))
	logs.Debugf("wsHeartbeat(5 second), timer id: ", p.timerID)
}

func (p *WsMng) heartbeatCB(params interface{}) {
	p.RWMutex.Lock()
	defer p.RWMutex.Unlock()

	wsMng.sn.Add(1)
	d, _ := json.Marshal(HeartbeatRequest{
		MsgHead: WsHead{
			InstanceID:  "sc-instance-ID",
			ResOrReq:    "req",
			RequestType: "ws-heartbeat",
			SerialNum:   wsMng.sn.Load(),
		},
	})

	for k, conn := range p.wsConnMap {
		if err := conn.WriteMessage(websocket.TextMessage, d); err != nil {
			logs.Errorf("ws check heartbeat to conn(%s) fail, err: %s", k.RemoteAddr().String(), err)
			chan2svc.DelWsServer(conn)
			conn.Close()
			delete(p.wsConnMap, k)
			continue
		}
	}
}

// http升级websocket协议的配置
var wsUpgrader = websocket.Upgrader{
	// 允许所有CORS跨域请求
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

func (p *WsMng) wsHandler(resp http.ResponseWriter, req *http.Request) *WsServer {
	var wsServer *WsServer = nil

	// 应答客户端告知升级连接为websocket
	wsSocket, err := wsUpgrader.Upgrade(resp, req, nil)
	if err != nil {
		logs.Errorf("wsHandler fail: %s", err.Error())
		return nil
	}

	wsServer = CreateWsServer(p.appCtxt, wsSocket)
	// wsServer.Start()
	p.RWMutex.Lock()
	defer p.RWMutex.Unlock()
	p.wsConnMap[wsSocket] = wsServer

	// // 处理器
	// go wsServer.procLoop()
	// // 读协程
	// go wsServer.wsReadLoop()
	// // 写协程
	// go wsServer.wsWriteLoop()

	return wsServer
}

func (p *WsMng) wsBroadcast(v interface{}) {
	d, e := json.Marshal(v)
	if e != nil {
		logs.Errorf("wsBroadcast to Marshal fail: %+v", v)
		return
	}

	p.RWMutex.RLock()
	defer p.RWMutex.RUnlock()
	for k, conn := range p.wsConnMap {
		if err := conn.WriteMessage(websocket.TextMessage, d); err != nil {
			logs.Warnf("wsBroadcast to writeMessage to (%s) fail: %s", k.RemoteAddr().String(), err)
			continue
		}
	}
}

func (p *WsMng) wsSend(conn *websocket.Conn, v interface{}) error {
	if conn == nil {
		return fmt.Errorf("wsSend to nil conn")
	}

	p.RWMutex.RLock()
	defer p.RWMutex.RUnlock()

	wsServer, ok := p.wsConnMap[conn]
	if !ok {
		logs.Warnf("wsSend to a unexist conn(%p)", conn)
		return fmt.Errorf("wsSend to a unexist conn(%p)", conn)
	}

	d, err := json.Marshal(v)
	if err != nil {
		logs.Errorf("wsSend to Marshal fail: %+v", v)
		return fmt.Errorf("wsSend to Marshal fail: %+v", v)
	}
	return wsServer.WriteMessage(websocket.TextMessage, d)
}

func (p *WsMng) Dump() string {
	out := fmt.Sprintln("WsMng ConnMap Len = ", len(p.wsConnMap))

	p.RWMutex.RLock()
	defer p.RWMutex.RUnlock()
	for _, v := range p.wsConnMap {
		out += fmt.Sprintln(v)
	}

	return out
}
